/*
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif

#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 2
#endif
 */

#include "glodef.h"
#include "errlog.h"
#include "grid3d.h"
#include "glovar.h"
#include "vecs.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>

/* The following are used in create_dir and open_dir
 * the ISO/ANSI C standard does not provide machinary for working with
 * files and directories at lower level.  Here we use the posix standard
 */

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/*
#include <sys/types.h>
 */
/* there is a bug in gcc and PATH_MAX doesn't get defined in limits.h
 * as it should according to ANSI.  It is defined in linux/limits.h.
 * In CYGWIN, however, the file linux/limits.h does not exist and PATH_MAX
 * is defined properly in limits.h
 */
/*
 * #ifndef __CYGWIN__
 * #include <linux/limits.h>
 * #endif
 */

#ifndef PATH_MAX
#define PATH_MAX        FILENAME_MAX
#endif

static size_t ignore;
static int ignor;

/* this file exports the following routines */
void save_ref_grid(void);
void load_grid(const char *path);


void load_state_time(const char *dir);
void load_adjoint_time(const char *dir);

void save_state_time(const char *dir);
void save_adjoint_time(const char *dir);

void save_nodes_time(const char *dir);
int load_nodes_time(const char *dir);


void create_dir(const char *path);
void rename_dir(const char *path);
/* void backup_data(void); */
int open_dir(const char *path);

/* the following routines and variables are local */
static void save_vector_bin(const vector v, FILE *F);
static void save_scalar_bin(const scalar s, FILE *F);

static void load_vector_bin(vector v, FILE *F);
static void load_scalar_bin(scalar s, FILE *F);

static FILE *open_file(const char *fname, const char *mode, int error);

static char dir_path[PATH_MAX]=".";
static char file_path[PATH_MAX];

void save_state_time(const char *dir)
{
    if(mpi_my_id!=0)
        return;

    LOG_MPI_WTIME_START;

    FILE *F;
    char buf[PATH_MAX];
    int digits=lround(floor(log10(total_ti)+1));

    assert(u!=0);
    assert(p!=0);
    assert(phi!=0);

    if(dir) open_dir(dir);
    sprintf(buf, "%0*d.bin", digits, curr_ti);
    F=open_file(buf, "wb", TRUE);
    save_vector_bin(u, F);
    save_scalar_bin(p, F);
    save_scalar_bin(phi, F);
    fclose(F);

    LOG_MPI_WTIME_STOP;
}

void save_adjoint_time(const char *dir)
{
    if(mpi_my_id!=0)
        return;

    LOG_MPI_WTIME_START;

    FILE *F;
    char buf[PATH_MAX];
    int digits=lround(floor(log10(total_ti)+1));

    assert(v!=0);
    assert(q!=0);
    assert(qhi!=0);

    if(dir) open_dir(dir);
    sprintf(buf, "%0*d.bin", digits, curr_ti);
    F=open_file(buf, "wb", TRUE);
    save_vector_bin(v, F);
    save_scalar_bin(q, F);
    save_scalar_bin(qhi, F);
    fclose(F);

    LOG_MPI_WTIME_STOP;
}

void save_nodes_time(const char *dir)
{
    if(mpi_my_id!=0)
        return;

    LOG_MPI_WTIME_START;

    FILE *F;
    char buf[PATH_MAX];
    int digits=lround(floor(log10(total_ti)+1));
    if(dir) open_dir(dir);
    sprintf(buf, "%0*d.bin", digits, curr_ti);
    F=open_file(buf, "wb", TRUE);
    {
        int i;
        node_t *n=grid->nodes;
        double C[3];
        for(i=0; i<grid->NN; i++, n++)
        {
            C[0]=n->coords[0];
            C[1]=n->coords[1];
            C[2]=n->coords[2];
            ignore=fwrite(C, sizeof(double), 3, F);
        }
    }
    fclose(F);

    LOG_MPI_WTIME_STOP;
}

void save_ref_grid(void)
{
    if(mpi_my_id!=0)
        return;

    FILE *F;
    F=open_file("ref_grid.bin", "wb", TRUE);
    save_grid_bin(grid, F);
    fclose(F);
}

void load_grid(const char *path)
{
    FILE *F;
    F=open_file(path, "rb", TRUE);
    load_grid_bin(grid, F);
    UpdateMeasures(grid);
    MakeGridNameFromPath(grid, path);
    LogGridStats(grid);
    fclose(F);
}

static FILE *open_file(const char *fname, const char *mode, int error)
{
    FILE *F;
    sprintf(file_path, "%s/%s", dir_path, fname);
    F=fopen(file_path, mode);
    // PrintLog("%s: open file %s with fileno %d.\n", __func__, file_path, fileno(F));
    if(F==NULL&&error)
        error_msg("open_file: %s\n", strerror(errno));
    return F;
}

void create_dir(const char *path)
{
    strncpy(dir_path, path, PATH_MAX);
    if(mpi_my_id!=0)
        return;

    static char cmd[PATH_MAX+10];
    const mode_t mod=S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;

    sprintf(cmd, "/bin/rm -r -f %s", path);
    int cmdret=system(cmd);
    (void)cmdret;

    if(mkdir(path, mod))
        warning_msg("create_dir(%s):%d:%s", path, errno, strerror(errno));

    return;
}

int open_dir(const char *path)
{
    int fd;
    fd=open(path, O_RDONLY);
    if(fd<0)
    {
        warning_msg("open_dir(%s): %s\n", path, strerror(errno));
        strncpy(dir_path, ".", PATH_MAX);
        return FALSE;
    }
    // PrintLog("%s: open dir %s with fileno %d.\n", __func__, path, fd);
    close(fd);
    strncpy(dir_path, path, PATH_MAX);
    return TRUE;
}

void rename_dir(const char *path)
{
    strncpy(dir_path, path, PATH_MAX);
    if(mpi_my_id!=0)
        return;

    static char cmd[2*PATH_MAX+10];
    sprintf(cmd, "mv -f %s %s", dir_path, path);
    ignor=system(cmd);
    return;
}

/*
 * void backup_data(void)
 * {
 * static char cmd[2*PATH_MAX+10];
 * static char back_dir[FILENAME_MAX];
 * const mode_t mod = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
 * strncpy(back_dir, dir_path, FILENAME_MAX);
 * strncat(back_dir, "/backup", FILENAME_MAX);
 * sprintf(cmd, "rm -r -f %s", back_dir);
 * system(cmd);
 * if (mkdir(back_dir, mod))
 * warning_msg("%s: %s", __func__, strerror(errno));
 * sprintf(cmd, "cp -f %s/""*.* %s/", dir_path, back_dir);
 * system(cmd);
 * return;
 * }
 */

/****************************************************************************/

static void save_vector_bin(const vector v, FILE *F)
{
    if(mpi_my_id!=0)
        return;

    // LOG_MPI_WTIME_START;
    if(F==NULL) return;
    int vN=get_N_vec(v);
    ignore=fwrite(&vN, sizeof(int), 1, F);
    use_vector(v)
    ignore=fwrite(vX, sizeof(double), vN, F);
    ignore=fwrite(vY, sizeof(double), vN, F);
    ignore=fwrite(vZ, sizeof(double), vN, F);
    done_vector(v)
    if(ferror(F)) perror(NULL);
    // LOG_MPI_WTIME_STOP;
    return;
}

static void save_scalar_bin(const scalar s, FILE *F)
{
    if(mpi_my_id!=0)
        return;

    // LOG_MPI_WTIME_START;
    if(F==NULL) return;
    int sN=get_N_scal(s);
    ignore=fwrite(&sN, sizeof(int), 1, F);
    use_scalar(s)
    ignore=fwrite(sF, sizeof(double), sN, F);
    done_scalar(s)
    if(ferror(F)) perror(NULL);
    // LOG_MPI_WTIME_STOP;
    return;
}


/****************************************************************************/
/*                load_xxx_bin                                              */

/****************************************************************************/

int load_nodes_time(const char *dir)
{

    LOG_MPI_WTIME_START;

    FILE *F;
    char buf[PATH_MAX];
    double *P;
    size_t ret;
    int digits=lround(floor(log10(total_ti)+1));
    if(dir) open_dir(dir);
    sprintf(buf, "%0*d.bin", digits, curr_ti);
    F=open_file(buf, "rb", FALSE);
    if(F==NULL)
        return FALSE;
    P=malloc(3*grid->NN*sizeof(double));
    if(P==NULL)
        error_msg("%s: memory allocation error.\n", __func__);
    ret=fread(P, sizeof(double), 3*grid->NN, F);
    if(ret<3*grid->NN)
    {
        warning_msg("%s: errno=%d: %s", __func__, errno, strerror(errno));
        free(P);
        fclose(F);
        return FALSE;
    }
    else
    {
        int i, j;
        node_t *n=grid->nodes;
        for(i=0; i<grid->NN; i++)
            for(j=0; j<3; j++)
                n[i].coords[j]=P[3*i+j];
    }
    free(P);
    fclose(F);
    LOG_MPI_WTIME_STOP;
    return TRUE;
}

void load_state_time(const char *dir)
{
    LOG_MPI_WTIME_START;
    FILE *F;
    char buf[PATH_MAX];
    int digits=lround(floor(log10(total_ti)+1));
    if(dir) open_dir(dir);
    sprintf(buf, "%0*d.bin", digits, curr_ti);
    F=open_file(buf, "rb", TRUE);
    load_vector_bin(u, F);
    load_scalar_bin(p, F);
    load_scalar_bin(phi, F);
    fclose(F);
    LOG_MPI_WTIME_STOP;
}

void load_adjoint_time(const char *dir)
{
    LOG_MPI_WTIME_START;
    FILE *F;
    char buf[PATH_MAX];
    int digits=lround(floor(log10(total_ti)+1));
    if(dir) open_dir(dir);
    sprintf(buf, "%0*d.bin", digits, curr_ti);
    F=open_file(buf, "rb", TRUE);
    load_vector_bin(v, F);
    load_scalar_bin(q, F);
    load_scalar_bin(qhi, F);
    fclose(F);
    LOG_MPI_WTIME_STOP;
}

static void load_vector_bin(vector v, FILE *F)
{
    int N;
    // LOG_MPI_WTIME_START;
    if(F==NULL) return;
    ignore=fread(&N, sizeof(int), 1, F);
    if(N!=get_N_vec(v))
        error_msg("load_vector_bin: dimensions don't match.\n");
    use_vector(v)
    ignore=fread(vX, sizeof(double), N, F);
    ignore=fread(vY, sizeof(double), N, F);
    ignore=fread(vZ, sizeof(double), N, F);
    done_vector(v)
    if(ferror(F)) perror(NULL);
    // LOG_MPI_WTIME_STOP;
    return;
}

static void load_scalar_bin(scalar s, FILE *F)
{
    int N;
    // LOG_MPI_WTIME_START;
    if(F==NULL) return;
    ignore=fread(&N, sizeof(int), 1, F);
    if(N!=get_N_scal(s))
        error_msg("%s: dimensions don't match.\n", __func__);
    use_scalar(s)
    ignore=fread(sF, sizeof(double), N, F);
    done_scalar(s)
    if(ferror(F)) perror(NULL);
    // LOG_MPI_WTIME_STOP;
    return;
}



